package com.zijidelu.luframework.utils.lang.reflect.bean;

import cn.hutool.core.map.reference.WeakKeyValueConcurrentMap;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ReflectUtil;
import com.esotericsoftware.reflectasm.ConstructorAccess;
import com.esotericsoftware.reflectasm.FieldAccess;
import com.esotericsoftware.reflectasm.MethodAccess;
import com.zijidelu.luframework.utils.CastUtil;
import lombok.Getter;
import org.apache.commons.lang3.function.TriFunction;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.*;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;

/**
 * 高性能反射工具类。
 * <p>
 * 此工具类专注于通过 getter/setter 和直接字段访问来操作对象，内部使用 ReflectASM 实现，
 * 并通过缓存方法索引以达到极致性能。
 * <p>
 * <strong>核心优化：</strong>在实例化时，会预先扫描并缓存所有 getter/setter 方法对应的数字索引。
 * 后续所有属性访问都将直接通过索引调用，完全避免了运行时的字符串查找开销，性能接近原生调用。
 * <p>
 * <strong>设计原则：</strong>完全封装了 ReflectASM 的实现细节，对外提供简洁、安全的 API。
 * <strong>注意：</strong>ReflectASM 只能访问非私有权限的字段和方法。
 *
 * @author ZIJIDELU
 * @date 2024/5/2 0:18
 */
public final class XReflectUtil<T> implements ReflectAccessor<T> {

    private static final Map<Class<?>, XReflectUtil<?>> CACHE = new WeakKeyValueConcurrentMap<>();
    // 无需处理的类
    private static final Set<Class<?>> NO_HANDLE_CLASSES = Set.of(Object.class, Enum.class);
    private final Object[] emptyArgs = new Object[0];

    @Getter
    private final Class<T> objClass;
    private final MethodAccess methodAccess;
    private final FieldAccess fieldAccess;
    private final MethodHandles.Lookup lookup = MethodHandles.lookup();

    // --- 核心缓存：统一了 getter/setter 和字段访问 ---
    @Getter
    private final Map<String, BeanProperty> beanPropertyCache;
    // --- 专门为 copyProperties 优化：纯数组遍历，无任何查找开销 ---
    private final BeanProperty[] beanPropertiesArray;
    // --- 构造函数缓存：按参数数量分组，提高查找效率 ---
    private final Map<Integer, List<BeanConstructor<T>>> constructorCache;
    // --- 无参构造函数缓存：直接调用 ---
    private final Function<Object[], T> noArgsConstructorCreator;
    // 属性复制缓存，targetClass -> BeanCopier
    private final Map<Class<?>, BeanCopier> beanCopierCache = new WeakKeyValueConcurrentMap<>();

    private XReflectUtil(Class<T> clazz) {
        this.objClass = clazz;
        this.methodAccess = MethodAccess.get(clazz);
        this.fieldAccess = FieldAccess.get(clazz);

        // 构建统一的方法和字段索引缓存
        this.beanPropertyCache = this.buildBeanPropertyCache(clazz);
        // 转换为数组和列表，用于 copyProperties 优化
        this.beanPropertiesArray = this.beanPropertyCache.values().toArray(new BeanProperty[0]);
        // 构建构造函数缓存
        this.constructorCache = this.buildConstructorCache(clazz);
        // 缓存无参构造
        this.noArgsConstructorCreator = this.constructorCache.get(0).getFirst().creator();
    }

    public static <T> XReflectUtil<T> of(Class<T> clazz) {
        Objects.requireNonNull(clazz, "Class cannot be null");
        return CastUtil.cast(CACHE.computeIfAbsent(clazz, XReflectUtil::new));
    }

    /**
     * 计算构造函数的创建器。
     *
     * @param constructor 构造函数
     * @param isPublic    是否为 public 构造函数
     * @return 构造函数的创建器
     */
    private Function<Object[], T> calcConstructorCreator(Class<T> clazz, Constructor<T> constructor, boolean isPublic) {
        // 公共无参构造
        if (isPublic && constructor.getParameterCount() == 0) {
            // public 无参构造函数使用ReflectAsm
            final ConstructorAccess<T> constructorAccess = ConstructorAccess.get(clazz);
            return args -> constructorAccess.newInstance();
        }

        // 动态构造器构建器
        final boolean compatible = DynamicConstructorBuilder.isCompatible(constructor);
        if (compatible) {
            // 有参构造函数使用动态构造器
            final DynamicConstructorBuilder<T> dynamicConstructorBuilder = DynamicConstructorBuilder.of(constructor);
            switch (constructor.getParameterCount()) {
                case 0 -> {
                    // 无参构造
                    final Supplier<T> constructorInvoker0 = dynamicConstructorBuilder.toConstructorInvoker0();
                    return args -> constructorInvoker0.get();
                }
                case 1 -> {
                    // 1参构造
                    Function<Object, T> constructorInvoker1 = dynamicConstructorBuilder.toConstructorInvoker1();
                    return args -> constructorInvoker1.apply(args[0]);
                }
                case 2 -> {
                    // 2参构造
                    BiFunction<Object, Object, T> constructorInvoker2 = dynamicConstructorBuilder.toConstructorInvoker2();
                    return args -> constructorInvoker2.apply(args[0], args[1]);
                }
                case 3 -> {
                    // 3参构造
                    TriFunction<Object, Object, Object, T> constructorInvoker3 = dynamicConstructorBuilder.toConstructorInvoker3();
                    return args -> constructorInvoker3.apply(args[0], args[1], args[2]);
                }
                default -> {
                    return args -> standardReflectNewInstance(constructor, args);
                }
            }
        }

        // 其他参数数量的构造函数使用标准反射
        return args -> standardReflectNewInstance(constructor, args);
    }

    /**
     * 构建构造函数缓存。
     * <p>
     * 此方法会扫描所有构造函数，统一使用 MethodHandle 创建高性能访问器，并按参数数量分组缓存。
     *
     * @param clazz 目标类
     * @return 构造函数缓存
     */
    private Map<Integer, List<BeanConstructor<T>>> buildConstructorCache(Class<T> clazz) {
        // 缓存构造函数，按参数数量分组
        final Map<Integer, List<BeanConstructor<T>>> cache = new HashMap<>();

        for (Constructor<T> constructor : ReflectUtil.getConstructors(clazz)) {
            final Class<?>[] paramTypes = constructor.getParameterTypes();
            final boolean isPublic = Modifier.isPublic(constructor.getModifiers());
            final Function<Object[], T> creator = calcConstructorCreator(clazz, constructor, isPublic);
            // 缓存构造函数
            cache.computeIfAbsent(paramTypes.length, k -> new ArrayList<>())
                    .add(new BeanConstructor<>(paramTypes, creator, isPublic));

        }

        return Collections.unmodifiableMap(cache);
    }

    /**
     * 使用标准反射创建实例。
     * <p>
     * 此方法会处理所有反射异常，将其包装为运行时异常抛出。
     *
     * @param constructor 构造函数
     * @param args        构造参数
     * @return 新实例
     */
    private static <T> T standardReflectNewInstance(Constructor<?> constructor, Object[] args) {
        try {
            return CastUtil.cast(constructor.newInstance(args));
        } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 构建统一的方法和字段索引缓存。
     * <p>
     * 此方法是性能和功能的核心。它会扫描所有公共方法，为符合 Java Bean 规范的 getter/setter
     * 创建基于 ReflectASM 的高性能访问器。对于没有 getter/setter 的字段，它会降级使用
     * MethodHandle 或 FieldAccess 进行访问。
     *
     * @param clazz 目标类
     * @return 包含所有属性访问器的不可变 Map
     */
    private Map<String, BeanProperty> buildBeanPropertyCache(Class<T> clazz) {
        // 获取类的 Lookup 实例，用于私有访问
        final MethodHandles.Lookup privateLookupIn;
        try {
            privateLookupIn = MethodHandles.privateLookupIn(clazz, lookup);
        } catch (IllegalAccessException e) {
            throw new RuntimeException("Failed to get private lookup for class: " + clazz.getName(), e);
        }

        final Map<String, Function<Object, Object>> getterCache = new HashMap<>();
        final Map<String, BiConsumer<Object, Object>> setterCache = new HashMap<>();

        // 1. 预缓存所有公共 getter/setter 方法
        final Method[] methods = ReflectUtil.getMethods(clazz);
        for (Method method : methods) {
            // 过滤非公共或抽象方法
            if (!Modifier.isPublic(method.getModifiers()) || Modifier.isAbstract(method.getModifiers())) {
                continue;
            }

            // 判断方法是否是无需处理的类所拥有
            if (NO_HANDLE_CLASSES.contains(method.getDeclaringClass())) {
                continue;
            }

            // 缓存公共方法索引
            final int index;
            try {
                index = methodAccess.getIndex(method.getName(), method.getParameterTypes());
            } catch (IllegalArgumentException e) {
                continue; // ReflectASM 无法处理的方法，跳过
            }

            if (method.getName().startsWith(ReflectAccessor.IS_PREFIX) && method.getParameterCount() == 0
                    && (method.getReturnType() == boolean.class || method.getReturnType() == Boolean.class)) {
                // 缓存 boolean 类型的 is 方法
                final String fieldName = ReflectAccessor.calcFieldNameWithIs(method.getName());
                getterCache.put(fieldName, (bean) -> methodAccess.invoke(bean, index));
            } else if (method.getName().startsWith(ReflectAccessor.GET_PREFIX) && method.getParameterCount() == 0
                    && method.getReturnType() != void.class) {
                // 缓存其他 getter 方法
                final String fieldName = ReflectAccessor.calcFieldNameWithGetter(method.getName());
                getterCache.putIfAbsent(fieldName, (bean) -> methodAccess.invoke(bean, index));
            } else if (method.getName().startsWith(ReflectAccessor.SET_PREFIX) && method.getParameterCount() == 1
                    && method.getReturnType() == void.class) {
                // 缓存 setter 方法
                final String fieldName = ReflectAccessor.calcFieldNameWithSetter(method.getName());
                setterCache.put(fieldName, (bean, value) -> methodAccess.invoke(bean, index, value));
            }
        }

        // 2. 遍历所有字段，为没有 getter/setter 的字段创建访问器
        final Map<String, BeanProperty> propertyCache = new HashMap<>();
        final Field[] fields = ReflectUtil.getFields(clazz);
        for (Field field : fields) {
            // 跳过静态字段
            if (Modifier.isStatic(field.getModifiers())) {
                continue;
            }

            final String fieldName = field.getName();

            // 获取字段的 getter 函数
            final Function<Object, Object> getter = getterCache.containsKey(fieldName)
                    ? getterCache.get(fieldName)
                    : createFieldGetter(field, privateLookupIn);

            // 获取字段的 setter 函数
            final BiConsumer<Object, Object> setter = setterCache.containsKey(fieldName)
                    ? setterCache.get(fieldName)
                    : createFieldSetter(field, privateLookupIn);

            propertyCache.put(fieldName, new BeanProperty(fieldName, field.getType(), getter, setter));
        }

        return Collections.unmodifiableMap(propertyCache);
    }

    /**
     * 创建字段的 getter 函数。
     * <p>
     * 如果字段是公共的，直接使用 ReflectASM 的 FieldAccess。否则，尝试使用 MethodHandle。
     *
     * @param field           目标字段
     * @param privateLookupIn 方法查找器
     * @return 字段值获取函数
     */
    private Function<Object, Object> createFieldGetter(Field field, MethodHandles.Lookup privateLookupIn) {
        final String fieldName = field.getName();

        // 如果字段是公共的，直接使用 ReflectASM 的 FieldAccess
        if (Modifier.isPublic(field.getModifiers())) {
            return obj -> fieldAccess.get(obj, fieldName);
        } else {
            try {
                // 尝试使用 MethodHandle 获取字段值
                final MethodHandle handle = privateLookupIn.findGetter(objClass, fieldName, field.getType())
                        .asType(MethodType.methodType(Object.class, Object.class));
                return obj -> {
                    try {
                        return handle.invokeExact(obj);
                    } catch (Throwable e) {
                        throw new RuntimeException("Failed to get field value via MethodHandle", e);
                    }
                };

            } catch (Exception e) {
                // 回退到反射获取字段值
                field.setAccessible(true);
                return obj -> {
                    try {
                        return field.get(obj);
                    } catch (IllegalAccessException ex) {
                        throw new RuntimeException("Failed to get field value via reflection", ex);
                    }
                };
            }

        }
    }

    /**
     * 创建字段的 setter 函数。
     * <p>
     * 如果字段是最终的，返回一个空操作函数。如果字段是公共的，直接使用 ReflectASM 的 FieldAccess。否则，尝试使用 MethodHandle。
     *
     * @param field           目标字段
     * @param privateLookupIn 方法查找器
     * @return 字段值设置函数
     */
    private BiConsumer<Object, Object> createFieldSetter(Field field, MethodHandles.Lookup privateLookupIn) {
        // 如果字段是最终的，返回一个空操作函数
        if (Modifier.isFinal(field.getModifiers())) {
            return (obj, value) -> { /* No-op for final fields */ };
        }
        // 如果字段是公共的，直接使用 ReflectASM 的 FieldAccess
        if (Modifier.isPublic(field.getModifiers())) {
            return (obj, value) -> fieldAccess.set(obj, field.getName(), value);
        } else {
            try {
                // 否则，尝试使用 MethodHandle
                final MethodHandle handle = privateLookupIn.findSetter(objClass, field.getName(), field.getType())
                        .asType(MethodType.methodType(void.class, Object.class, Object.class));
                return (obj, value) -> {
                    try {
                        handle.invokeExact(obj, value);
                    } catch (Throwable e) {
                        throw new RuntimeException("Failed to set field value via MethodHandle", e);
                    }
                };
            } catch (Exception e) {
                field.setAccessible(true);
                return (obj, value) -> {
                    try {
                        field.set(obj, value);
                    } catch (IllegalAccessException ex) {
                        throw new RuntimeException("Failed to set field value via reflection", ex);
                    }
                };
            }
        }
    }

    // --- 核心公共 API ---

    @Override
    public T newInstance() {
        return noArgsConstructorCreator.apply(null);
    }

    @Override
    public T newInstance(final Object... args) {
        // 获取所有兼容的构造函数
        final List<BeanConstructor<T>> beanConstructors = constructorCache.get(args.length);
        if (beanConstructors == null) {
            throw new IllegalArgumentException("No compatible constructor found for args: " + Arrays.toString(args));
        }

        final Class<?>[] argTypes = ClassUtil.getClasses(args);

        // 正确的嵌套循环遍历
        for (BeanConstructor<T> candidate : beanConstructors) {
            // 使用预存的数组，避免 toArray() 开销
            if (ClassUtil.isAllAssignableFrom(argTypes, candidate.paramTypesArray())) {
                return candidate.creator().apply(args);
            }
        }

        throw new IllegalArgumentException("No compatible constructor found for args: " + Arrays.toString(argTypes));
    }

    @Override
    public void copyProperties(Object source, Object target) {
        if (source == null || target == null) return;
        if (objClass == target.getClass()) {
            // 源目标类相同，直接使用字段访问器
            for (BeanProperty beanProperty : this.beanPropertiesArray) {
                beanProperty.setter().accept(target, beanProperty.getter().apply(source));
            }
        } else {
            // 源目标类不同，使用反射访问器
            final XReflectUtil<?> targetAccessor = XReflectUtil.of(target.getClass());
            final BeanCopier beanCopier = beanCopierCache.computeIfAbsent(target.getClass(), key -> BeanCopier.of(this, targetAccessor));
            for (BeanProperty beanProperty : beanCopier.beanProperties()) {
                beanProperty.setter().accept(target, beanProperty.getter().apply(source));
            }
        }
    }

    @Override
    public Object getFieldValue(final Object obj, final String fieldName) {
        BeanProperty property = beanPropertyCache.get(fieldName);
        if (property != null) {
            return property.getter().apply(obj);
        }
        throw new RuntimeException("Field '" + fieldName + "' not found in class " + objClass.getName());
    }

    @Override
    public void setFieldValue(final Object obj, final String fieldName, final Object value) {
        BeanProperty property = beanPropertyCache.get(fieldName);
        if (property != null) {
            property.setter().accept(obj, value);
            return;
        }
        throw new RuntimeException("Field '" + fieldName + "' not found in class " + objClass.getName());
    }

    @Override
    public Object invoke(final Object obj, final String methodName) {
        return methodAccess.invoke(obj, methodName);
    }

    @Override
    public Object invoke(final Object obj, final String methodName, final Object... args) {
        return methodAccess.invoke(obj, methodName, args);
    }

    @Override
    public Object invoke(final Object obj, final String methodName, final Class<?>[] paramTypes, final Object... args) {
        return methodAccess.invoke(obj, methodName, paramTypes, args);
    }
}
